/*
 Copyright (C) 1996-1997 GX Media, Inc.

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

 See the GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

 */

/*
 * View.cpp
 */

#include "stdafx.h"

#include "View.h"

bool *ent_texd;

//==================== View ====================

bool View::showEntities = true;
int View::manipVertex = -1;
const Edge3d *View::manipEdge = NULL;
const GPolygon *View::manipFace = NULL;

int View::renderColors[VRC_NUMRENDERCOLORS];

const View *View::renderView = NULL;
QDraw *View::renderQDraw = NULL;
bool View::skipRender = false;
bool View::skipSlctObjs = false;
Points3d View::vBuffer;
Point3d View::ptsBuf1[48];
Point3d View::ptsBuf2[48];
Object *View::pCrossHair = NULL;
Object *View::pCrossHairNorth = NULL;
//Object *View::pLeakGeomObj = NULL;
Object *View::pLeakObj = NULL;
bool View::aprxWFRender = false;

View::View(QDraw *pQdraw, Object &viewObject, const Selector &selector)
{
	selectorPtr = &selector;

	SetWindowWidth(1);
	SetClipDepth(1000.0f);
	SetNewView(viewObject, selector);

	showEntities = true;
	displayPts = 0;

	qdraw = pQdraw;

	for (int i = 0; i < VRC_NUMRENDERCOLORS; i++)
		renderColors[i] = i;
}

View::~View(void)
{
	ShowCrossHair(false);
}

View &
View::operator=(const View &src)
{
	if (this == &src)
		return *this;

	LFatal();

	// ElmNode::operator=(src);
	TransSpace::operator=(src);

	qdraw = src.qdraw;
	viewObj = src.viewObj;
	perspective = src.perspective;
	clipView = src.clipView;
	winWidth = src.winWidth;
	selectorPtr = src.selectorPtr;
	return *this;
}

//===== View Settings =====

void View::SetNewView(Object &viewObject, const Selector &selector, bool reset,
		Vector3d *posVec, SphrVector *orientVec, bool perspectiveView,
		int windowWidth, float viewDepth)
{
	// Precondition.
	ASSERT(&viewObject == &(selector.GetScope()));

	viewObj = &viewObject;
	selectorPtr = &selector;

	if (reset)
	{
		Vector3d pVec(0.0f, 0.0f, 0.0f);
		SphrVector oriVec(0.0f, 0.0f, 0.0f);

		SetPosition(posVec ? *posVec : pVec);
		SetOrientation(orientVec ? *orientVec : oriVec);
		SetPerspective(perspectiveView);
		if (windowWidth != 0)
			SetWindowWidth(windowWidth);
		if (viewDepth > 0.0f)
			SetClipDepth(viewDepth);
	}

	DisplayFaceSelect(false);
}

void View::SetPerspective(bool perspectiveView)
{
	Vector3d cMin = clipView.GetMinClipEdges();
	Vector3d cMax = clipView.GetMaxClipEdges();

	if (perspectiveView && cMin.GetY() < 1.0f)
		cMin.SetY(1.0f);

	perspective = perspectiveView;
	clipView.SetClipVolume(cMin, cMax, perspective);
}

float View::GetClipDepth(void) const
{
	// Sanity
	ASSERT(perspective ||
			(clipView.GetMaxClipEdges()).GetY() ==
			-(clipView.GetMinClipEdges()).GetY());

	return (clipView.GetMaxClipEdges()).GetY();
}

void View::SetClipDepth(float depth)
{
	// Sanity.
	ASSERT(depth >= 1.0 && depth <= 10000.0f);

	Vector3d cMin = clipView.GetMinClipEdges();
	Vector3d cMax = clipView.GetMaxClipEdges();

	cMax.SetY(depth);
	cMin.SetY(perspective ? 1.0f : -depth);
	clipView.SetClipVolume(cMin, cMax, perspective);
}

void View::SetWindowWidth(int width)
{
	Vector3d cMin = clipView.GetMinClipEdges();
	Vector3d cMax = clipView.GetMaxClipEdges();

	winWidth = (float) width;
	cMin.SetX(-winWidth);
	cMin.SetZ(-winWidth);
	cMax.SetX(winWidth);
	cMax.SetZ(winWidth);
	clipView.SetClipVolume(cMin, cMax, perspective);
}

void View::MapViewSpaceOrigin(float &scrnX, float &scrnY)
{
	// Sanity.
	ASSERT(perspective == false);

	Matrix44 trans;
	Vector3d ov(0.0f, 0.0f, 0.0f);

	CalInvTransSpaceMatrix(trans.SetIdentity());
	trans.Transform(ov);
	scrnX = ov.GetX();
	scrnY = ov.GetZ();
}

//===== Object Selection ===== 

Line &
View::SetRay(float scrnX, float scrnY, Line &ray, bool relView) const
{
	Vector3d p0, v0;
	Matrix44 trans;

	if (perspective)
	{
		p0.NewVector(0.0f, 0.0f, 0.0f);
		v0.NewVector(scrnX / winWidth, 1.0f, scrnY / winWidth);
	}
	else
	{
		p0.NewVector(scrnX, 0.0f, scrnY);
		v0.NewVector(0.0f, 1.0f, 0.0f);
	}

	if (relView)
	{
		v0.AddVector(p0);
		TransSpace::CalTransSpaceMatrix(trans.SetIdentity());
		trans.Transform(p0);
		trans.Transform(v0);
		v0.SubVector(p0);
	}

	return ray.NewLine(p0, v0);
}

bool View::IsMultiSelectionHit(float scrnX, float scrnY) const
{
	Line slctRay;
	float rangeBegin, rangeEnd, z;
	bool ok;
	Object *tmp;

	if (selectorPtr->GetNumMSelectedObjects() == 0)
		return NULL;

	SetRay(scrnX, scrnY, slctRay, false);
	ok = clipView.ClipRay(slctRay, rangeBegin, rangeEnd);
	ASSERT(ok);
	SetRay(scrnX, scrnY, slctRay, true);

	IterLinkList<ObjectPtr> iter(selectorPtr->GetMSelectedObjects());
	iter.Reset();
	while (!iter.IsDone())
	{
		tmp = (iter.GetNext())->GetPtr();
		if (selectorPtr->IsSelectionHit(tmp, slctRay, rangeBegin, rangeEnd, z))
			return true;
	}
	return false;
}

Object *
View::SelectObject(float scrnX, float scrnY, int funcIndex) const
{
	Line slctRay;
	float rangeBegin, rangeEnd;
	bool ok;
	Object *rtnVal = NULL;

	SetRay(scrnX, scrnY, slctRay, false);
	ok = clipView.ClipRay(slctRay, rangeBegin, rangeEnd);
	ASSERT(ok);
	SetRay(scrnX, scrnY, slctRay, true);

	if (funcIndex == 0)
		rtnVal = selectorPtr->GetSNextSelect(slctRay, rangeBegin, rangeEnd);
	else if (funcIndex == 1)
		rtnVal = selectorPtr->GetSPrevSelect(slctRay, rangeBegin, rangeEnd);
	else if (funcIndex == 2)
		rtnVal = selectorPtr->GetMNextSelect(slctRay, rangeBegin, rangeEnd);
	else if (funcIndex == 3)
		rtnVal = selectorPtr->GetMPrevSelect(slctRay, rangeBegin, rangeEnd);

	return rtnVal;
}

int View::DragSelectMultiObjects(float scrnX1, float scrnY1, float scrnX2,
		float scrnY2, LinkList<ObjectPtr> &selectBuf) const
{
	// Set up a clip volume
	Vector3d cMin, cMax;
	Clip clipVol;

	if (perspective)
	{
		// Hacked the Clip class.
		// Passing in bounding ratios instead of bounding vectors.
		cMin.NewVector(scrnX1 / winWidth, (clipView.GetMinClipEdges()).GetY(),
				scrnY1 / winWidth);
		cMax.NewVector(scrnX2 / winWidth, (clipView.GetMaxClipEdges()).GetY(),
				scrnY2 / winWidth);
	}
	else
	{
		cMin.NewVector(scrnX1, (clipView.GetMinClipEdges()).GetY(), scrnY1);
		cMax.NewVector(scrnX2, (clipView.GetMaxClipEdges()).GetY(), scrnY2);
	}
	clipVol.SetClipVolume(cMin, cMax, perspective);

	// Set up the view transformation.
	Matrix44 trans;
	CalInvTransSpaceMatrix(trans.SetIdentity());
	viewObj->SetTransformation(trans);

	// Iter through children and find those in clip vol.
	Object *pObj;
	IterLinkList<Object> *pIter = &(viewObj->GetIterChildren());
	pIter->Reset();
	while (!pIter->IsDone())
	{
		pObj = pIter->GetNext();
		if (clipVol.IsObjectInVolume(*pObj))
			selectBuf.AppendNode(*(new ObjectPtr(pObj)));
	}

	return selectBuf.NumOfElm();
}

//===== Brush Manipulation Selection =====
// #define PTSEL_EPSILON 10

int View::SelectVertex(float scrnX, float scrnY, float epsilon,
		Vector3d *pManipDotPos)
{
	// Sanity.
	ASSERT(displayPts == 1);

	int i, rtnVal;
	Matrix44 trans;
	float div, ptX, ptY, diff, depth;

	ASSERT(selectorPtr->GetNumMSelectedObjects() == 1);
	Object *objNode = (selectorPtr->GetMSelectedObjects())[0].GetPtr();

	ASSERT(objNode->HasBrush());
	Geometry *brush = &(objNode->GetBrush());

	if (clipView.BoundObject(*objNode) == -1)
		return -1;

	TransSpace::CalInvTransSpaceMatrix(trans.SetIdentity());
	objNode->CalTransSpaceMatrix(trans);
	brush->TransformVertexPts(trans, vBuffer, manipVertex);

	// Assume Points3d::DeletePoint() is never used.
	rtnVal = -1;
	depth = (clipView.GetMaxClipEdges()).GetY();
	for (i = 0; i < brush->GetNumVertices(); i++)
	{
		if (clipView.ClipPoint(vBuffer[i]))
			continue;
		if (!perspective)
		{
			ptX = vBuffer[i].GetX() - scrnX;
			ptY = vBuffer[i].GetZ() - scrnY;
		}
		else
		{
			div = winWidth / vBuffer[i].GetY();
			ptX = vBuffer[i].GetX() * div - scrnX;
			ptY = vBuffer[i].GetZ() * div - scrnY;
		}
		diff = (float) sqrt(ptX * ptX + ptY * ptY);
		if (diff <= epsilon && vBuffer[i].GetY() <= depth)
		{
			depth = vBuffer[i].GetY();
			rtnVal = i;
			if (pManipDotPos != NULL)
				*pManipDotPos = vBuffer[i];
		}
	}

	return rtnVal;
}

const Edge3d *
View::SelectEdge(float scrnX, float scrnY, float epsilon,
		Vector3d *pManipDotPos)
{
	// Sanity.
	ASSERT(displayPts == 2);

	int i, index;
	Matrix44 trans;
	float div, ptX, ptY, diff, depth;

	ASSERT(selectorPtr->GetNumMSelectedObjects() == 1);
	Object *objNode = (selectorPtr->GetMSelectedObjects())[0].GetPtr();

	ASSERT(objNode->HasBrush());
	Geometry *brush = &(objNode->GetBrush());

	if (clipView.BoundObject(*objNode) == -1)
		return NULL;

	TransSpace::CalInvTransSpaceMatrix(trans.SetIdentity());
	objNode->CalTransSpaceMatrix(trans);
	brush->TransformEdgePts(trans, vBuffer, manipEdge);

	index = -1;
	depth = (clipView.GetMaxClipEdges()).GetY();
	for (i = 0; i < brush->GetNumEdges(); i++)
	{
		if (clipView.ClipPoint(vBuffer[i]))
			continue;
		if (!perspective)
		{
			ptX = vBuffer[i].GetX() - scrnX;
			ptY = vBuffer[i].GetZ() - scrnY;
		}
		else
		{
			div = winWidth / vBuffer[i].GetY();
			ptX = vBuffer[i].GetX() * div - scrnX;
			ptY = vBuffer[i].GetZ() * div - scrnY;
		}
		diff = (float) sqrt(ptX * ptX + ptY * ptY);
		if (diff <= epsilon && vBuffer[i].GetY() <= depth)
		{
			depth = vBuffer[i].GetY();
			index = i;
			if (pManipDotPos != NULL)
				*pManipDotPos = vBuffer[i];
		}
	}
	// Sanity.
	ASSERT(index >= -1 || index < brush->GetNumEdges());

	return (index == -1 ? NULL : &(brush->GetEdgeList())[index]);
}

const GPolygon *
View::SelectFace(float scrnX, float scrnY, float epsilon,
		Vector3d *pManipDotPos)
{
	// Sanity.
	ASSERT(displayPts == 3);

	int i, index;
	Matrix44 trans;
	float div, ptX, ptY, diff, depth;

	ASSERT(selectorPtr->GetNumMSelectedObjects() == 1);
	Object *objNode = (selectorPtr->GetMSelectedObjects())[0].GetPtr();

	ASSERT(objNode->HasBrush());
	Geometry *brush = &(objNode->GetBrush());

	if (clipView.BoundObject(*objNode) == -1)
		return NULL;

	TransSpace::CalInvTransSpaceMatrix(trans.SetIdentity());
	objNode->CalTransSpaceMatrix(trans);
	brush->TransformFacePts(trans, vBuffer, manipFace);

	index = -1;
	depth = (clipView.GetMaxClipEdges()).GetY();
	for (i = 0; i < brush->GetNumFaces(); i++)
	{
		if (clipView.ClipPoint(vBuffer[i]))
			continue;
		if (!perspective)
		{
			ptX = vBuffer[i].GetX() - scrnX;
			ptY = vBuffer[i].GetZ() - scrnY;
		}
		else
		{
			div = winWidth / vBuffer[i].GetY();
			ptX = vBuffer[i].GetX() * div - scrnX;
			ptY = vBuffer[i].GetZ() * div - scrnY;
		}
		diff = (float) sqrt(ptX * ptX + ptY * ptY);
		if (diff <= epsilon && vBuffer[i].GetY() <= depth)
		{
			depth = vBuffer[i].GetY();
			index = i;
			if (pManipDotPos != NULL)
				*pManipDotPos = vBuffer[i];
		}
	}
	// Sanity.
	ASSERT(index >= -1 || index < brush->GetNumFaces());

	return (index == -1 ? NULL : &(brush->GetFace(index)));
}

void View::DisplayNoPts(void)
{
	displayPts = 0;
	manipVertex = -1;
	manipEdge = NULL;
	manipFace = NULL;
}

void View::DisplayVertexPts(int vertex)
{
	// PreCondition
	// ASSERT(selectorPtr->GetNumMSelectedObjects() == 1);
	// ASSERT((selectorPtr->GetMSelectedObjects())[0].GetPtr()->HasBrush());

	displayPts = 1;
	manipVertex = vertex;
}

void View::DisplayEdgePts(const Edge3d *edge)
{
	// PreCondition
	// ASSERT(selectorPtr->GetNumMSelectedObjects() == 1);
	// ASSERT((selectorPtr->GetMSelectedObjects())[0].GetPtr()->HasBrush());

	displayPts = 2;
	manipEdge = edge;
}

void View::DisplayFacePts(const GPolygon *face)
{
	// PreCondition
	// ASSERT(selectorPtr->GetNumMSelectedObjects() == 1);
	// ASSERT((selectorPtr->GetMSelectedObjects())[0].GetPtr()->HasBrush());

	displayPts = 3;
	manipFace = face;
}

//===== Crosshair =====

bool View::IsCrossHairShown(void)
{
	ASSERT((!pCrossHair && !pCrossHairNorth) || (pCrossHair && pCrossHairNorth));
	return (pCrossHair != NULL);
}

void View::ShowCrossHair(bool show)
{
	// Sanity.
	ASSERT((!pCrossHair && !pCrossHairNorth) || (pCrossHair && pCrossHairNorth));

	Entity *entPtr = NULL;

	if (show && pCrossHair == NULL)
	{
		pCrossHair = new Object();
		entPtr = new Entity(wxT("cross1"));
		pCrossHair->SetEntity(entPtr);
		pCrossHairNorth = new Object();
		entPtr = new Entity(wxT("cross2"));
		pCrossHairNorth->SetEntity(entPtr);
	}
	else if (!show && pCrossHair != NULL)
	{
		delete pCrossHair;
		pCrossHair = NULL;
		delete pCrossHairNorth;
		pCrossHairNorth = NULL;
	}
}

void View::SetCrossHairPos(const Vector3d &posVec)
{
	// Sanity.
	ASSERT((!pCrossHair && !pCrossHairNorth) || (pCrossHair && pCrossHairNorth));

	if (pCrossHair == NULL)
		return;

	pCrossHair->SetPosition(posVec);
	pCrossHairNorth->SetPosition(posVec);
}

Vector3d View::GetCrossHairPos(void)
{
	// PreCondition.
	ASSERT(pCrossHair && pCrossHairNorth->GetParentPtr());

	return pCrossHair->GetPosition();
}

//===== Leak Geometry =====
const Object *
View::GetLeakObject(void)
{
	return pLeakObj;
}

void View::SetLeakObject(Object *pNewObj)
{
	if (pLeakObj != NULL)
		delete pLeakObj;

	pLeakObj = pNewObj;
}

//===== Entities WireFrame Approximation =====

void View::ApproxItems(bool approximate)
{
	aprxWFRender = approximate;
}

//===== Color Settings =====
int View::GetColor(int colorIndex)
{
	// Sanity.
	ASSERT(colorIndex >= 0 && colorIndex < VRC_NUMRENDERCOLORS);

	return renderColors[colorIndex];
}

int View::SetColor(int colorIndex, int color)
{
	// Sanity.
	ASSERT(colorIndex >= 0 && colorIndex < VRC_NUMRENDERCOLORS);

	int rtnVal = renderColors[colorIndex];
	renderColors[colorIndex] = color;

	return rtnVal;
}

//===== Object Rendering =====

void View::RenderWireFrame(bool renderWorld, bool renderSelected,
		bool renderCrossHair) const
{
	// Sanity Check.
	ASSERT(viewObj != NULL); ASSERT(renderView == NULL);

	renderView = this;
	renderQDraw = qdraw;

	int oldNormColor, oldEntColor;

	if (renderWorld)
	{
		// Draw the world first.  Skip selected objects.
		skipSlctObjs = true;
		if(renderQDraw->UseHardware())
			viewObj->PreOrderCondApply(RenderObjWireFrameHardware);
		else
			viewObj->PreOrderCondApply(RenderObjWireFrame);
	}

	// Draw the crosshair.
	if (renderCrossHair && (pCrossHair || pCrossHairNorth))
	{
		oldEntColor = SetColor(VRC_ENTITY, GetColor(VRC_CROSSHAIR));

		ASSERT(pCrossHair->GetParentPtr() == NULL);
		pCrossHair->SetParentPtr(viewObj);
		if(renderQDraw->UseHardware())
			RenderObjWireFrameHardware(*pCrossHair);
		else
			RenderObjWireFrame(*pCrossHair);
		pCrossHair->SetParentPtr(NULL);

		SetColor(VRC_ENTITY, GetColor(VRC_CROSSHAIRNORTH));

		ASSERT(pCrossHairNorth->GetParentPtr() == NULL);
		pCrossHairNorth->SetParentPtr(viewObj);
		if(renderQDraw->UseHardware())
			RenderObjWireFrameHardware(*pCrossHairNorth);
		else
			RenderObjWireFrame(*pCrossHairNorth);
		pCrossHairNorth->SetParentPtr(NULL);

		SetColor(VRC_ENTITY, oldEntColor);
	}

	// Draw the additional objs. (leak geometry...)
	if (renderWorld && pLeakObj)
	{
		int oldBrushColor = SetColor(VRC_NORMAL, GetColor(VRC_MANIPDOT));

		ASSERT(pLeakObj->GetParentPtr() == NULL);
		pLeakObj->SetParentPtr(viewObj);
		if(renderQDraw->UseHardware())
			pLeakObj->PreOrderCondApply(RenderObjWireFrameHardware);
		else
			pLeakObj->PreOrderCondApply(RenderObjWireFrame);
		pLeakObj->SetParentPtr(NULL);

		SetColor(VRC_NORMAL, oldBrushColor);
	}

	// Draw the selected object.
	if (renderSelected)
	{
		if (!renderWorld)
		{
			// Need to set viewObj's trans matrix.

			Matrix44 trans;
			CalInvTransSpaceMatrix(trans.SetIdentity());
			viewObj->SetTransformation(trans);

			// What does this do?????
			skipRender = true;
			if(renderQDraw->UseHardware())
				viewObj->PreOrderCondApply(RenderObjWireFrameHardware);
			else
				viewObj->PreOrderCondApply(RenderObjWireFrame);
			skipRender = false;
		}

		// Draw the (multi) selected objects next.
		Object *rObjPtr;
		int numSlctObjs = selectorPtr->GetNumMSelectedObjects();
		skipSlctObjs = false;

		if (numSlctObjs > 0)
		{
			oldNormColor = SetColor(VRC_NORMAL, GetColor(VRC_SELECT));
			oldEntColor = SetColor(VRC_ENTITY, GetColor(VRC_SELECT));

			IterLinkList<ObjectPtr> iter(selectorPtr->GetMSelectedObjects());
			iter.Reset();
			while (!iter.IsDone())
			{
				rObjPtr = (iter.GetNext())->GetPtr();
				if(renderQDraw->UseHardware())
					rObjPtr->PreOrderCondApply(RenderObjWireFrameHardware);
				else
					rObjPtr->PreOrderCondApply(RenderObjWireFrame);
			}
			SetColor(VRC_NORMAL, oldNormColor);
			SetColor(VRC_ENTITY, oldEntColor);
		}

		// Draw any manipulation dots and outline faces.
		if (numSlctObjs == 1 && rObjPtr->HasBrush())
		{
			if (dispFaceSlct)
			{
				if(renderQDraw->UseHardware())
					RenderSlctFaceOutlineHardware(*rObjPtr);
				else
					RenderSlctFaceOutline(*rObjPtr);
			}
			if (displayPts != 0)
				DisplayManipPts();
		}

		// Draw the single (hot) selected object last.
		if ((rObjPtr = selectorPtr->GetSSelectedObject()) != NULL)
		{
			oldNormColor = SetColor(VRC_NORMAL, GetColor(VRC_HOTSELECT));
			oldEntColor = SetColor(VRC_ENTITY, GetColor(VRC_HOTSELECT));
			if(renderQDraw->UseHardware())
				rObjPtr->PreOrderCondApply(RenderObjWireFrameHardware);
			else
				rObjPtr->PreOrderCondApply(RenderObjWireFrame);
			SetColor(VRC_NORMAL, oldNormColor);
			SetColor(VRC_ENTITY, oldEntColor);
		}
	}

	renderView = NULL;
	renderQDraw = NULL;
}

bool View::RenderObjWireFrame(Object &objNode)
{
	Matrix44 trans;
	bool lineClip;
	int bound;

	ASSERT(!objNode.HasBrush() || !objNode.IsItemNode());

	// Check to see if objNode is one of the selected objects.
	if (skipSlctObjs)
	{
		const Selector *slctPtr = renderView->selectorPtr;

		/*
		 if (slctPtr->GetNumMSelectedObjects() > 0) {
		 IterLinkList<ObjectPtr> oIter(slctPtr->GetMSelectedObjects());
		 oIter.Reset();
		 while (!oIter.IsDone()) {
		 if ((oIter.GetNext())->GetPtr() == &objNode)
		 return false;
		 }
		 }
		 */
		if (objNode.IsSelected())
			return false;

		if (slctPtr->GetSSelectedObject() == &objNode)
			return false;
	}

	// Calculate the transformation matrix.
	if (&objNode == renderView->viewObj)
	{
		renderView->CalInvTransSpaceMatrix(trans.SetIdentity());
		objNode.SetTransformation(trans);
	}
	else if (!objNode.IsItemNode())
	{
		ASSERT(!objNode.IsRoot());

		trans = (objNode.GetParent()).GetTransformation();
		objNode.CalTransSpaceMatrix(trans);
		objNode.SetTransformation(trans);
	}
	else
	{ // Account for item scaling problem inside other objects.
		ASSERT(!objNode.IsRoot());

		if (!renderView->showEntities)
			return false;

		Matrix44 tr;
		SphrVector oriVec;
		Vector3d ov, fv, tv;

		trans = (objNode.GetParent()).GetTransformation();
		objNode.CalTransSpaceMatrix(trans);

		trans.Transform(ov, Vector3d::origVec);
		(trans.Transform(fv, Vector3d::yAxisVec)).SubVector(ov);
		(trans.Transform(tv, Vector3d::zAxisVec)).SubVector(ov);
		tr.SetRotate(oriVec.NewVector(fv, tv));
		(trans.SetTranslation(ov)).Multiply(tr);

		objNode.SetTransformation(trans);
	}

	// Boundary check the object.
	bound = (renderView->clipView).BoundObject(objNode);
	if (bound == 1)
		lineClip = false;
	else if (bound == 0)
		lineClip = true;
	else
		// bound == -1
		return false;

	// Prepare to render wireframe.
	const LinkList<Edge3d> *listPtr;

	if (objNode.HasBrush())
	{
		Geometry *brush = &(objNode.GetBrush());
		brush->Transform(objNode.GetTransformation(), vBuffer);
		listPtr = &(brush->GetEdgeList());

		renderView->qdraw->Color(GetColor(VRC_NORMAL));
	}
	else if (objNode.IsItemNode())
	{
		const WireFrameGeom *wfg = &(objNode.GetItemWireFrame());
		if (!aprxWFRender || objNode.IsSelected() || &objNode == pCrossHair
				|| &objNode == pCrossHairNorth || &objNode == pLeakObj)
		{
			wfg->TransformWireFrame(objNode.GetTransformation(), vBuffer);
			listPtr = &(wfg->GetWireFrameEdgeList());
		}
		else
		{
			wfg->TransformApproxBox(objNode.GetTransformation(), vBuffer);
			listPtr = &(wfg->GetApproxBoxEdgeList());
		}

		renderView->qdraw->Color(GetColor(VRC_ENTITY));
	}
	else
	{
		return true;
	}

	if (skipRender)
		return false;

	// Render the geometry.
	Point3d pt1, pt2;
	Edge3d *edgePtr;
	IterLinkList<Edge3d> edgeIter(*listPtr);

	for (edgeIter.Reset(); !edgeIter.IsDone();)
	{
		edgePtr = edgeIter.GetNext();
		pt1 = vBuffer[edgePtr->GetStartPoint()];
		pt2 = vBuffer[edgePtr->GetEndPoint()];

		// Clip line.
		if (lineClip && !(renderView->clipView).ClipLine(pt1, pt2))
			continue;

		if (!renderView->perspective)
		{
			renderView->qdraw->RenderLine(pt1.GetX(), pt1.GetZ(), pt2.GetX(),
					pt2.GetZ());
		}
		else
		{
			float div1 = renderView->winWidth / pt1.GetY();
			float div2 = renderView->winWidth / pt2.GetY();
			renderView->qdraw->RenderLine(pt1.GetX() * div1, pt1.GetZ() * div1,
					pt2.GetX() * div2, pt2.GetZ() * div2);
		}
	}

	return true;
}

bool View::RenderObjWireFrameHardware(Object &objNode)
{
	Matrix44 trans;
	int bound;

	ASSERT(!objNode.HasBrush() || !objNode.IsItemNode());

	// Check to see if objNode is one of the selected objects.
	if (skipSlctObjs)
	{
		const Selector *slctPtr = renderView->selectorPtr;

		/*
		 if (slctPtr->GetNumMSelectedObjects() > 0) {
		 IterLinkList<ObjectPtr> oIter(slctPtr->GetMSelectedObjects());
		 oIter.Reset();
		 while (!oIter.IsDone()) {
		 if ((oIter.GetNext())->GetPtr() == &objNode)
		 return false;
		 }
		 }
		 */
		if (objNode.IsSelected())
			return false;

		if (slctPtr->GetSSelectedObject() == &objNode)
			return false;
	}

	// Calculate the transformation matrix.
	if (&objNode == renderView->viewObj)
	{
		renderView->CalInvTransSpaceMatrix(trans.SetIdentity());
		objNode.SetTransformation(trans);
	}
	else if (!objNode.IsItemNode())
	{
		ASSERT(!objNode.IsRoot());

		trans = (objNode.GetParent()).GetTransformation();
		objNode.CalTransSpaceMatrix(trans);
		objNode.SetTransformation(trans);
	}
	else
	{ // Account for item scaling problem inside other objects.
		ASSERT(!objNode.IsRoot());

		if (!renderView->showEntities)
			return false;

		Matrix44 tr;
		SphrVector oriVec;
		Vector3d ov, fv, tv;

		trans = (objNode.GetParent()).GetTransformation();
		objNode.CalTransSpaceMatrix(trans);

		trans.Transform(ov, Vector3d::origVec);
		(trans.Transform(fv, Vector3d::yAxisVec)).SubVector(ov);
		(trans.Transform(tv, Vector3d::zAxisVec)).SubVector(ov);
		tr.SetRotate(oriVec.NewVector(fv, tv));
		(trans.SetTranslation(ov)).Multiply(tr);

		objNode.SetTransformation(trans);
	}

	// Boundary check the object.
	bound = renderView->clipView.BoundObject(objNode);
	if (bound == -1)
		return false;

	// Prepare to render wireframe.
	int ir, ig, ib;
	const LinkList<Edge3d> *listPtr;
	const Points3d *vertices = NULL;
	const GeomWFVertex *wireVertices = NULL;

	if (objNode.HasBrush())
	{
		Geometry *brush = &(objNode.GetBrush());
		vertices = &brush->GetVertices();
		listPtr = &brush->GetEdgeList();

		renderQDraw->GetColor(GetColor(VRC_NORMAL), ir, ig, ib);
	}
	else if (objNode.IsItemNode())
	{
		const WireFrameGeom *wfg = &(objNode.GetItemWireFrame());
		if (!aprxWFRender || objNode.IsSelected() || &objNode == pCrossHair
				|| &objNode == pCrossHairNorth || &objNode == pLeakObj)
		{
			wireVertices = wfg->GetWireFrameVertices();
			listPtr = &wfg->GetWireFrameEdgeList();
		}
		else
		{
			vertices = &wfg->GetApproxBoxVertices();
			listPtr = &wfg->GetApproxBoxEdgeList();
		}

		renderQDraw->GetColor(GetColor(VRC_ENTITY), ir, ig, ib);
	}
	else
	{
		return true;
	}

	if (skipRender)
		return false;

	// Sanity check.
	ASSERT(vertices != NULL || wireVertices != NULL);

	// Render the geometry.
	TriVertex vertex;
	Edge3d *edgePtr;
	IterLinkList<Edge3d> edgeIter(*listPtr);

	vertex.r = ir/255.0f;
	vertex.g = ig/255.0f;
	vertex.b = ib/255.0f;

	renderQDraw->ChangeTexture(NULL);
	renderQDraw->BeginObject(&objNode, PT_LINES);
	for (edgeIter.Reset(); !edgeIter.IsDone();)
	{
		edgePtr = edgeIter.GetNext();
		if(vertices)
		{
			const Point3d &pt1 = vertices->GetPoint(edgePtr->GetStartPoint());
			const Point3d &pt2 = vertices->GetPoint(edgePtr->GetEndPoint());

			vertex.x = pt1.GetX();
			vertex.y = pt1.GetY();
			vertex.z = pt1.GetZ();
			renderQDraw->SendVertex(vertex);

			vertex.x = pt2.GetX();
			vertex.y = pt2.GetY();
			vertex.z = pt2.GetZ();
			renderQDraw->SendVertex(vertex);
		}
		else if(wireVertices)
		{
			const GeomWFVertex &pt1 = wireVertices[edgePtr->GetStartPoint()];
			const GeomWFVertex &pt2 = wireVertices[edgePtr->GetEndPoint()];

			vertex.x = pt1.x;
			vertex.y = pt1.y;
			vertex.z = pt1.z;
			renderQDraw->SendVertex(vertex);

			vertex.x = pt2.x;
			vertex.y = pt2.y;
			vertex.z = pt2.z;
			renderQDraw->SendVertex(vertex);
		}
	}

	renderQDraw->EndObject();

	return true;
}

void View::RenderSlctFaceOutline(Object &objNode) const
{
	// Sanity.
	ASSERT(dispFaceSlct); ASSERT(!objNode.IsItemNode()); ASSERT(objNode.HasBrush()); ASSERT(GetSelectFace() != NULL);

	bool lineClip;
	int bound;

	// Boundary check the object.
	bound = clipView.BoundObject(objNode);
	if (bound == 1)
		lineClip = false;
	else if (bound == 0)
		lineClip = true;
	else
		// bound == -1
		return;

	Geometry *brush = &(objNode.GetBrush());
	IterLinkList<Edge3d> eIter((GetSelectFace())->GetEdgeList());
	Point3d pt1, pt2;
	Edge3d *ePtr;

	// Assume objNode has the right transformation from RenderObjWF().
	brush->Transform(objNode.GetTransformation(), vBuffer);

	qdraw->UseColor(GetColor(VRC_FACESELECT));
	eIter.Reset();
	while (!eIter.IsDone())
	{
		ePtr = eIter.GetNext();
		pt1 = vBuffer[ePtr->GetStartPoint()];
		pt2 = vBuffer[ePtr->GetEndPoint()];

		// Clip line.
		if (lineClip && !clipView.ClipLine(pt1, pt2))
			continue;

		if (!perspective)
		{
			qdraw->RenderLine(pt1.GetX(), pt1.GetZ(), pt2.GetX(), pt2.GetZ());
		}
		else
		{
			float div1 = winWidth / pt1.GetY();
			float div2 = winWidth / pt2.GetY();
			qdraw->RenderLine(pt1.GetX() * div1, pt1.GetZ() * div1, pt2.GetX()
					* div2, pt2.GetZ() * div2);
		}
	}
}

void View::RenderSlctFaceOutlineHardware(Object &objNode) const
{
	// Sanity.
	ASSERT(dispFaceSlct); ASSERT(!objNode.IsItemNode()); ASSERT(objNode.HasBrush()); ASSERT(GetSelectFace() != NULL);

	bool lineClip;
	int bound;

	// Boundary check the object.
	bound = clipView.BoundObject(objNode);
	if (bound == -1)
		return;

	Geometry *brush = &(objNode.GetBrush());
	IterLinkList<Edge3d> eIter((GetSelectFace())->GetEdgeList());
	Point3d pt1, pt2;
	Edge3d *ePtr;
	TriVertex vertex;

	// Assume objNode has the right transformation from RenderObjWF().
	qdraw->ChangeTexture(NULL);
	qdraw->BeginObject(&objNode, PT_LINES);
	const Points3d &vertices = brush->GetVertices();

	int ir, ig, ib;
	qdraw->GetColor(GetColor(VRC_FACESELECT), ir, ig, ib);
	vertex.r = ir/255.0f;
	vertex.g = ig/255.0f;
	vertex.b = ib/255.0f;

	eIter.Reset();
	while (!eIter.IsDone())
	{
		ePtr = eIter.GetNext();
		pt1 = vertices[ePtr->GetStartPoint()];
		pt2 = vertices[ePtr->GetEndPoint()];

		// Clip line.
		if (lineClip && !clipView.ClipLine(pt1, pt2))
			continue;

		vertex.x = pt1.GetX();
		vertex.y = pt1.GetY();
		vertex.z = pt1.GetZ();
		qdraw->SendVertex(vertex);

		vertex.x = pt2.GetX();
		vertex.y = pt2.GetY();
		vertex.z = pt2.GetZ();
		qdraw->SendVertex(vertex);
	}

	qdraw->EndObject();
}

void View::DisplayManipPts(void) const
{
	// Sanity.
	ASSERT(displayPts > 0 && displayPts <= 3);

	float div;
	int i, numPts;
	Matrix44 trans;

	ASSERT(selectorPtr->GetNumMSelectedObjects() == 1);
	Object *objNode = (selectorPtr->GetMSelectedObjects())[0].GetPtr();

	ASSERT(objNode->HasBrush());
	Geometry *brush = &(objNode->GetBrush());

	trans = objNode->GetTransformation();
	if (displayPts == 1)
	{
		brush->TransformVertexPts(trans, vBuffer, manipVertex);
		numPts = (manipVertex != -1 ? 1 : brush->GetNumVertices());
	}
	else if (displayPts == 2)
	{
		brush->TransformEdgePts(trans, vBuffer, manipEdge);
		numPts = (manipEdge != NULL ? 1 : brush->GetNumEdges());
	}
	else
	{ // displayPts == 3
		brush->TransformFacePts(trans, vBuffer, manipFace);
		numPts = (manipFace != NULL ? 1 : brush->GetNumFaces());
	}

	qdraw->Color(GetColor(VRC_MANIPDOT));

	for (i = 0; i < numPts; i++)
	{
		if (clipView.ClipPoint(vBuffer[i]))
			continue;
		if (!perspective)
		{
			qdraw->RenderPoint(vBuffer[i].GetX(), vBuffer[i].GetZ());
		}
		else
		{
			div = winWidth / vBuffer[i].GetY();
			qdraw->RenderPoint(vBuffer[i].GetX() * div, vBuffer[i].GetZ() * div);
		}
	}
}

//===== Solid Rendering: Z Buffer =====

// Hack.
static bool renderTMap = false;
static Matrix44 svTrans;

static void CalSVTrans(Object &objNode)
{

	if (objNode.IsRoot())
	{
		svTrans.SetIdentity();
	}
	else
	{
		CalSVTrans(objNode.GetParent());
		objNode.CalTransSpaceMatrix(svTrans);
	}
}

void View::RenderSolid(bool tmap) const
{
	// Sanity Check.
	ASSERT(viewObj != NULL);

	renderView = this;
	renderQDraw = qdraw;
	renderTMap = tmap;

	if (renderQDraw->UseHardware())
	{
		viewObj->PreOrderCondApply(RenderObjSolidHardware);
	}
	else
	{
		CalSVTrans(*viewObj);

		Matrix44 viewTrans;
		CalTransSpaceMatrix(viewTrans.SetIdentity());
		svTrans.Multiply(viewTrans);

		viewObj->PreOrderCondApply(RenderObjSolid);

		qdraw->ZBufRender();
	}

	if (selectorPtr->GetNumMSelectedObjects() == 1)
	{
		Object *rObjPtr = (selectorPtr->GetMSelectedObjects())[0].GetPtr();
		if (rObjPtr->HasBrush() && dispFaceSlct)
		{
			if(renderQDraw->UseHardware())
				RenderSlctFaceOutlineHardware(*rObjPtr);
			else
				RenderSlctFaceOutline(*rObjPtr);
		}
	}

	renderView = NULL;
	renderQDraw = NULL;
}

extern bool *ent_texd;

bool View::RenderObjSolid(Object &objNode)
{
	Matrix44 trans;
	bool polyClip;
	int bound;

	//  if(!*ent_texd && renderTMap && objNode.IsItemNode())
	//  if(renderTMap && objNode.IsItemNode())
	//    return false;

	// Calculate transformation matrix.
	if (&objNode == renderView->viewObj)
	{
		if (renderTMap)
		{
			renderView->TransSpace::CalTransSpaceMatrix(trans.SetIdentity());
			objNode.SetInvTransformation(trans);
		}

		renderView->TransSpace::CalInvTransSpaceMatrix(trans.SetIdentity());
		objNode.SetTransformation(trans);
	}
	else if (!objNode.IsItemNode())
	{
		if (renderTMap)
		{
			objNode.CalInvTransSpaceMatrix(trans.SetIdentity());
			trans.Multiply(objNode.GetParent().GetInvTransformation());
			objNode.SetInvTransformation(trans);
		}

		trans = (objNode.GetParent()).GetTransformation();
		objNode.CalTransSpaceMatrix(trans);
		objNode.SetTransformation(trans);
	}
	else
	{ // Account for item scaling problem inside other objects.
		// Sanity Check.
		ASSERT(!objNode.IsRoot());

		if (!renderView->showEntities)
			return false;

		Matrix44 tr;
		SphrVector oriVec;
		Vector3d ov, fv, tv;

		trans = (objNode.GetParent()).GetTransformation();
		objNode.CalTransSpaceMatrix(trans);

		trans.Transform(ov, Vector3d::origVec);
		(trans.Transform(fv, Vector3d::yAxisVec)).SubVector(ov);
		(trans.Transform(tv, Vector3d::zAxisVec)).SubVector(ov);
		tr.SetRotate(oriVec.NewVector(fv, tv));
		(trans.SetTranslation(ov)).Multiply(tr);

		objNode.SetTransformation(trans);
	}

	// Boundary check the object.
	bound = renderView->clipView.BoundObject(objNode);
	if (bound == 1)
		polyClip = false;
	else if (bound == 0)
		polyClip = true;
	else
		// bound == -1
		return false;

	// Prepare for rendering.
	IterLinkList<PlaneNode> *planeIter;

	if (objNode.HasBrush())
	{
		const Geometry *brush = &(objNode.GetBrush());
		planeIter = new IterLinkList<PlaneNode> (brush->GetPlaneList());
		brush->Transform(trans, vBuffer);
	}
	else if (objNode.IsItemNode())
	{
		const WireFrameGeom *wfg = &(objNode.GetItemWireFrame());
		planeIter = new IterLinkList<PlaneNode> (wfg->GetPlaneList());
		wfg->TransformApproxBox(trans, vBuffer);
	}
	else
	{
		return true;
	}

	// Solid render the brush.
	static const Vector3d oVec(0.0f, 0.0f, 0.0f);
	static Vector3d nVec(0.0f, 1.0f, 0.0f), pVec, cVec;
	static Plane facePlane(nVec, oVec);
	IterLinkList<Edge3d> *edgeIter;
	PlaneNode *pNodePtr;
	Point3d *vPtr, *fvPtr;
	int i, numPts;
	static int color = 0;
	float div;
	ZPoint zpts[3];
	bool lockTex;
	Matrix44 absTrans;

	absTrans.Multiply(svTrans, trans);
	trans.Transform(cVec, oVec);

	planeIter->Reset();
	while (!planeIter->IsDone())
	{
		pNodePtr = planeIter->GetNext();
		trans.Transform(nVec, (pNodePtr->GetPlane()).GetNorm());
		nVec.SubVector(cVec);
		trans.Transform(pVec, (pNodePtr->GetPlane()).GetPoint());
		facePlane.NewPlane(nVec, pVec);
		if (facePlane.PointInPlane(oVec) <= 0.0f)
			continue; // Back face.  Skip.

		// Set Texture/Color
		lockTex = (pNodePtr->GetPolygon()).GetTexturePtr()->IsTexLocked();

		nVec = (pNodePtr->GetPlane()).GetNorm();

		if (!lockTex)
		{
			absTrans.Transform(pVec, oVec);
			(absTrans.Transform(nVec)).SubVector(pVec);
		}

		bool texd = renderQDraw->ZBufSetup(&objNode,
				(pNodePtr->GetPolygon()).GetTexturePtr(), nVec);

		// Copy the vertices to local buffer.
		numPts = (pNodePtr->GetPolygon()).GetNumSides();
		ASSERT(numPts <= 32); // Sanity.
		edgeIter = new IterLinkList<Edge3d> (
				pNodePtr->GetPolygon().GetEdgeList());
		edgeIter->Reset();
		for (fvPtr = ptsBuf1; !edgeIter->IsDone(); fvPtr++)
			*fvPtr = vBuffer[(edgeIter->GetNext())->GetStartPoint()];
		delete edgeIter;

		// Clip Polygon.
		fvPtr = ptsBuf1;
		if (polyClip && renderView->clipView.ClipPolygon(numPts, ptsBuf1,
				ptsBuf2, &fvPtr) < 3)
			continue;

		ASSERT(numPts <= 48); // Sanity.

		// Break into triangles and render.
		vPtr = fvPtr;

		div = renderView->winWidth / fvPtr->GetY();
		zpts[0].x = fvPtr->GetX() * div;
		zpts[0].y = fvPtr->GetZ() * div;
		zpts[0].z = fvPtr->GetY();

		if (texd)
		{
			if (lockTex)
				(objNode.GetInvTransformation()).Transform(pVec, *vPtr);
			else
				svTrans.Transform(pVec, *vPtr);

			renderQDraw->ZBufCalcST(pVec.GetX(), pVec.GetY(), pVec.GetZ(),
					zpts[0].s, zpts[0].t);
		}
		fvPtr++;

		div = renderView->winWidth / fvPtr->GetY();
		zpts[2].x = fvPtr->GetX() * div;
		zpts[2].y = fvPtr->GetZ() * div;
		zpts[2].z = fvPtr->GetY();

		if (texd)
		{
			if (lockTex)
				(objNode.GetInvTransformation()).Transform(pVec, *fvPtr);
			else
				svTrans.Transform(pVec, *fvPtr);

			renderQDraw->ZBufCalcST(pVec.GetX(), pVec.GetY(), pVec.GetZ(),
					zpts[2].s, zpts[2].t);
		}
		fvPtr++;

		for (i = 2; i < numPts; i++, fvPtr++)
		{
			zpts[1].x = zpts[2].x;
			zpts[1].y = zpts[2].y;
			zpts[1].z = zpts[2].z;

			if (!fvPtr->GetY())
				continue;

			div = renderView->winWidth / fvPtr->GetY();
			zpts[2].x = fvPtr->GetX() * div;
			zpts[2].y = fvPtr->GetZ() * div;
			zpts[2].z = fvPtr->GetY();

			if (texd)
			{
				zpts[1].s = zpts[2].s;
				zpts[1].t = zpts[2].t;

				if (lockTex)
					(objNode.GetInvTransformation()).Transform(pVec, *fvPtr);
				else
					svTrans.Transform(pVec, *fvPtr);

				renderQDraw->ZBufCalcST(pVec.GetX(), pVec.GetY(), pVec.GetZ(),
						zpts[2].s, zpts[2].t);
			}

			renderQDraw->ZBufTriangle(zpts);
		}
	}
	delete planeIter;

	return true;
}

inline void GetFaceColor(Object *obj, int face, float &r, float &g, float &b)
{
	short color = (((size_t)obj + face * 10) % 0xFFFF);
	r = (float)((((color >> 11) & 31) << 3) / 256.0f);
	g = (float)((((color >> 5) & 63) << 2) / 256.0f);
	b = (float)((((color >> 0) & 31) << 3) / 256.0f);
}

bool View::RenderObjSolidHardware(Object &objNode)
{
	Matrix44 trans;
	bool selected = false;
	bool selectFace = false;
	int selectFaceIndex = -1;

	// Draw selected objects with a different color.
	if (objNode.IsSelected())
		selected = true;

	if (renderView->selectorPtr->GetSSelectedObject() == &objNode)
		selected = true;

	if (selected && renderView->selectorPtr->GetNumMSelectedObjects() == 1)
	{
		Object *rObjPtr = renderView->selectorPtr->GetMSelectedObjects()[0].GetPtr();
		if (rObjPtr->HasBrush() && renderView->dispFaceSlct)
		{
			selectFace = true;
			selectFaceIndex = renderView->selectorPtr->GetSelectFaceIndex();
		}
	}

	// Calculate transformation matrix.
	if (&objNode == renderView->viewObj)
	{
		renderView->TransSpace::CalInvTransSpaceMatrix(trans.SetIdentity());
		objNode.SetTransformation(trans);
	}
	else if (!objNode.IsItemNode())
	{
		trans = (objNode.GetParent()).GetTransformation();
		objNode.CalTransSpaceMatrix(trans);
		objNode.SetTransformation(trans);
	}
	else
	{ // Account for item scaling problem inside other objects.
		// Sanity Check.
		ASSERT(!objNode.IsRoot());

		if (!renderView->showEntities)
			return false;

		Matrix44 tr;
		SphrVector oriVec;
		Vector3d ov, fv, tv;

		trans = (objNode.GetParent()).GetTransformation();
		objNode.CalTransSpaceMatrix(trans);

		trans.Transform(ov, Vector3d::origVec);
		(trans.Transform(fv, Vector3d::yAxisVec)).SubVector(ov);
		(trans.Transform(tv, Vector3d::zAxisVec)).SubVector(ov);
		tr.SetRotate(oriVec.NewVector(fv, tv));
		(trans.SetTranslation(ov)).Multiply(tr);

		objNode.SetTransformation(trans);
	}

	// Boundary check the object.
	int bound = renderView->clipView.BoundObject(objNode);
	if (bound == -1)
		return false;

	// Prepare for rendering.
	IterLinkList<PlaneNode> *planeIter = NULL;

	const Points3d *vertices = NULL;
	if (objNode.HasBrush())
	{
		Geometry *brush = &objNode.GetBrush();
		planeIter = new IterLinkList<PlaneNode> (brush->GetPlaneList());
		vertices = &brush->GetVertices();
	}
	else if (objNode.IsItemNode())
	{
		const WireFrameGeom *wfg = &objNode.GetItemWireFrame();
		planeIter = new IterLinkList<PlaneNode> (wfg->GetPlaneList());
		vertices = &wfg->GetApproxBoxVertices();
	}
	else
	{
		return true;
	}


	Vector3d normalVector;
	IterLinkList<Edge3d> *edgeIter;
	PlaneNode *pNodePtr;
	TriVertex vertex;
	float r, g, b;
	float sr, sg, sb;
	int planeId = 0;

	// Read the selection color.
	if(selected)
	{
		int ir, ig, ib;
		renderView->qdraw->GetColor(GetColor(VRC_SELECT), ir, ig, ib);
		sr = ir/255.0f;
		sg = ig/255.0f;
		sb = ib/255.0f;
	}
	else
	{
		GetFaceColor(&objNode, 0, r, g, b);
	}

	planeIter->Reset();
	while (!planeIter->IsDone())
	{
		pNodePtr = planeIter->GetNext();

		ASSERT(pNodePtr);
		normalVector = pNodePtr->GetPlane().GetNorm();
		// Prepare ST texture coordinate generation.
		FaceTex *faceTex = pNodePtr->GetPolygon().GetTexturePtr();
		Texture *texture = faceTex ? faceTex->GetTexture() : NULL;
		bool textured = renderQDraw->ZBufSetup(&objNode, faceTex, normalVector);
		if(textured)
		{
			renderQDraw->ChangeTexture(faceTex);
			r = g = b = 1.0f;
		}
		else
		{
			renderQDraw->ChangeTexture(NULL);
		}

		// Use the selection color.
		if(selected)
		{
			r = sr; g = sg; b = sb;

			// Use a different color for the current selected face.
			if(selectFace && selectFaceIndex == planeId)
			{
				int ir, ig, ib;
				renderView->qdraw->GetColor(GetColor(VRC_FACESELECT), ir, ig, ib);
				r = ir/255.0f;
				g = ig/255.0f;
				b = ib/255.0f;
			}
		}

		edgeIter = new IterLinkList<Edge3d> (
						pNodePtr->GetPolygon().GetEdgeList());
		edgeIter->Reset();

		renderQDraw->BeginObject(&objNode, PT_TRIANGLE_FAN);
		while(!edgeIter->IsDone())
		{
			Vector3d pos = vertices->GetPoint(edgeIter->GetNext()->GetStartPoint());
			vertex.x = pos.GetX();
			vertex.y = pos.GetY();
			vertex.z = pos.GetZ();
			vertex.r = r;
			vertex.g = g;
			vertex.b = b;
			vertex.nx = normalVector.GetX();
			vertex.ny = normalVector.GetY();
			vertex.nz = normalVector.GetZ();

			if(textured)
			{
				// Calculate st coordinates.
				renderQDraw->ZBufCalcST(vertex.x, vertex.y, vertex.z, vertex.s, vertex.t);
				vertex.s /= texture->width;
				vertex.t /= texture->height;
			}
			else
			{
				vertex.s = 0.0f;
				vertex.t = 0.0f;
			}

			renderQDraw->SendVertex(vertex);
		}
		renderQDraw->EndObject();
		delete edgeIter;
		planeId++;
	}


	delete planeIter;

	return true;
}
